package com.haven123.cloudapp.webbase.web.log;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Tony_Du on 2018/10/31 0031.
 */
@Aspect
@Component
public class ControllerLogAspect {
    @Pointcut("execution(* com.haven123.cloudapp.*.controller..*.*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        Object target = pjp.getTarget();
        Logger log = LoggerFactory.getLogger(target.getClass());

        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();

        String requestURI = request.getRequestURI();
        String requestMethod = request.getMethod();
        String requestQueryString = request.getQueryString();

        String key = requestURI + "." + requestMethod;
        if (!URI_PARAM_NAME_MAP.containsKey(key)) {
            synchronized (this) {
                if (!URI_PARAM_NAME_MAP.containsKey(key)) {
                    MethodSignature signature = (MethodSignature) pjp.getSignature();
                    String[] parameterNames = signature.getParameterNames();
                    URI_PARAM_NAME_MAP.put(key, parameterNames);
                }
            }
        }
        String[] parameterNames = URI_PARAM_NAME_MAP.get(key);

        Object[] args = pjp.getArgs();
        String params;
        if ("GET".equals(requestMethod)
                || "DELETE".equals(requestMethod)) {
            params = requestQueryString;
        } else {
            if (args.length == 0) {
                params = StringUtils.EMPTY;
            } else {
                StringBuilder sb = new StringBuilder();
                int index = -1;
                for (Object arg : args) {
                    index++;
                    if (arg instanceof ModelMap
                            || arg instanceof ServletRequest
                            || arg instanceof ServletResponse) {
                        continue;
                    }
                    sb.append(parameterNames[index]).append("=").append(JSON.toJSONString(arg)).append(",");
                }
                sb.deleteCharAt(sb.length() - 1);
                params = sb.toString();
            }
        }

        log.info("req {} {} {}", requestMethod, requestURI, params);
        try {
            Object result = pjp.proceed();
            log.info("resp {} {} {}", requestMethod, requestURI, JSON.toJSONString(result));
            return result;
        } catch (Throwable ex) {
            log.error("err {} {} {}", requestMethod, requestURI, ex);
            throw ex;
        }
    }


    private Map<String, String[]> URI_PARAM_NAME_MAP = new ConcurrentHashMap<>();
}
